# -*- coding: utf-8 -*-

import tempfile
import subprocess
import re
from time import time

IDP_PATH = './runidp.sh'

class KnightException(Exception):
    pass

class Knight():
    """Brave knight, our agent, 
       KB file handling, IDP connection"""

    theory      = ''                     # path to agents theory
    usertypes   = ''                     # user defined part of theory
    structure   = ''                     # last structure 
    output      = ''                     # last IDP output
    idp         = './bin/runidp.sh'      # path to idp       
    
    time      = 0
    senses    = []    # Senses tuples
    moves     = []    # Moves mapings
    
    idptime   = 0.0   # Time used by IDP3 call  

    #'NextAction[:CommonVoc::Action] = Climb'
    ap = re.compile(r"NextAction\[:\w+::Action\] = (Forward|Left|Right|Grab|Shoot|Climb)")
    utp = re.compile(r'/\*\s*structure\s*Input\s*:\s*AgentVoc\s*{(.+)}\s*\*/', re.DOTALL)

    def __init__(self, theory, idp=None):
        self.theory = theory
        self.usertypes = self.parseTheory()
        if idp: self.idp    = idp

    def parseTheory(self):
        """Search for user types"""
        if self.theory:
            types = self.utp.search(file(self.theory,'r').read())
            if types:  
                return types.group(1)
            else:
                return r'//Nothing'
    
    def genStructere(self):
        self.structure = """
structure Input : AgentVoc {
  // Constant part  
  Action = { Forward ; Left ; Right ; Grab ; Shoot ; Climb }
  forward = Forward
  left = Left
  right = Right
  grab = Grab
  shoot = Shoot
  climb = Climb
  
  Percept = {Stench ; Bats ; Breeze ; Glitter ; Bump ; Scream ; Light }
  stench = Stench
  bats = Bats
  breeze = Breeze
  glitter = Glitter
  bump = Bump
  scream = Scream
  light = Light
  
  // Part modified after each move
  // Here is stored information about previous actions of your agent.

  Time = {%s}              // number of actions taken
  Past = {%s}              // as Time without the current time 
  // Moves 
  %s

  // Sneses
  %s

  // Copied from %s
  %s
}
""" % ('0..%d' % self.time if self.time else '0',
       '%s' % ('0..%d' % (self.time - 1) if self.time > 0 else ''),
       'Moves = {\n\t' + ';\n\t'.join(self.moves) + '}\n' if self.moves else '// nothing yet\n',
       'Senses = {\n' + ';\n\t'.join(self.senses) + '  }',
       self.theory,
       self.usertypes
      )
        return self.structure

    def getFullIDP(self):
        return "%s\n%s\n%s\n" % (
            file('common.idp', 'r').read(),
            file(self.theory, 'r').read(),
            self.genStructere()
        )
    
    def runtheory(self):
        """Runs IDP and extracts action"""
        # TODO probably there is simpler way
        
        inp = tempfile.NamedTemporaryFile('w', delete=False)
        out = tempfile.NamedTemporaryFile()
        err = tempfile.NamedTemporaryFile()

        # save input        
        inp.write(self.genStructere())
        inp.flush()

        # run IDP
        try:
            params = [self.idp, 'common.idp', self.theory, inp.name]
            start = time()            
            subprocess.call(params, stdout=out, stderr=err)
            self.idptime = time() - start
        except Exception as e:
            raise KnightException('MissingIDP', str(params))

        # extract action
        err.seek(0)
        e = err.read()
        if 'Error' in e: raise KnightException('IDP3Error', e)

        out.seek(0)
        self.output = out.read()
        action = self.ap.search(self.output)
        if action:  
            return action.group(1)
        else:
            raise KnightException('NoAction', self.output)
    
    def dump_output(self):
        return self.output
    
    def add_percepts(self, percepts):
        for p in percepts:
            self.senses.append('%d, %s' % (self.time, p))
    
    def add_action(self, action):
        self.moves.append('%d -> %s' % (self.time, action))
        self.time += 1

    def act(self,percepts, action=None):
        # update percepts
        self.add_percepts(percepts)

        # calculate action (unless given - needed for debug)
        if not action:
            action = self.runtheory()

        # update time and moves
        self.add_action(action)
        return action

if __name__ == "__main__":
    k = Knight('Agents/runner.idp', IDP_PATH)
    print k.act(set())

